﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading;
using UnityEngine;

namespace MLog.Core.Appender {
    public class LoggerWorker {
        private readonly string _baseName;

        private readonly string       _dir;
        private readonly Queue        _logQueue = Queue.Synchronized(new Queue());
        private          int          _lastDay  = -1;
        private          Thread       _workThread;
        private          StreamWriter _writer;

        public LoggerWorker(string dir, string baseName) {
            _dir      = dir;
            _baseName = baseName;
        }

        public bool Running { set; get; }

        public string Dir {
            get { return _dir; }
        }

        public string BaseName {
            get { return _baseName; }
        }

        public void Put(string text) {
            if (Running) _logQueue.Enqueue(text);
        }

        private void Run() {
            while (Running) {
                while (_logQueue.Count > 0) {
                    if (IsNewDay()) {
                        CloseStream();
                        _writer  = CreateFile();
                        _lastDay = DateTime.Today.Day;
                    }

                    var text = _logQueue.Dequeue();
                    _writer.WriteLine(text);
                }

                if (_writer != null) _writer.Flush();
            }

            CloseStream();
        }

        private void CloseStream() {
            if (_writer != null) {
                _writer.Flush();
                _writer.Close();
                _writer = null;
            }
        }

        public void Start() {
            lock (this) {
                if (Running) {
                    Debug.LogWarning("MLog: log thread already running.");
                    return;
                }

                _workThread = new Thread(Run) {
                    IsBackground = true,
                    Name         = string.Format("MLog-{0}:{1}", _dir, _baseName)
                };
                _workThread.Start();
                Running = true;
            }
        }

        public void Stop() {
            Running = false;
        }

        private bool IsNewDay() {
            if (_lastDay == -1) return true;

            return DateTime.Today.Day != _lastDay;
        }

        public StreamWriter CreateFile() {
            if (!Directory.Exists(_dir)) {
                Directory.CreateDirectory(_dir);
            }

            var path = Path.Combine(_dir, string.Format("{0}-{1:yyyy-MM-dd}.log", _baseName, DateTime.Today));
            _writer = new StreamWriter(new FileStream(path, FileMode.Append, FileAccess.Write,FileShare.Read), Encoding.UTF8);
            return _writer;
        }
    }

    public static class Workers {
        private static readonly List<LoggerWorker> _workers = new List<LoggerWorker>();

        public static LoggerWorker GetOrCreateWorker(string dir, string baseName) {
            LoggerWorker worker = null;
            for (var i = 0; i < _workers.Count; i++) {
                var w = _workers[i];
                if (w.Dir.Equals(dir) && w.BaseName.Equals(baseName)) {
                    worker = w;
                    break;
                }
            }

            if (worker == null) {
                worker = new LoggerWorker(dir, baseName);
                _workers.Add(worker);
                worker.Start();
            }

            return worker;
        }

        public static void Clear() {
            for (var i = 0; i < _workers.Count; i++) _workers[i].Stop();

            _workers.Clear();
        }
    }
}